<?php
/**
 * +--------------------------------------------------------+
 * @Created by Phpstorm.
 * @Category SwooleMysql.class.php
 * @Depiction
 * @Author hwy
 * @Date 2021/4/15  14:54
 * +--------------------------------------------------------+
 */

namespace app\Lib\Org;

class SwooleMysql
{
    /**
     * 配置
     * @access private
     * @var array
     */
    private $config = [
        //MySQL IP地址
        'host'        => '127.0.0.1',
        //数据用户
        'user'        => 'root',
        //数据库密码
        'password'    => 'root',
        //数据库名
        'database'    => 'laravel_admin',
        //MySQL端口 默认3306 可选参数
        'port'        => 3306,
        //建立连接超时时间
        'timeout'     => 3,
        //字符集
        'charset'     => 'utf8',
        //开启严格模式，query方法返回的数据也将转为强类型
        'strict_type' => false,
        //开启fetch模式, 可与pdo一样使用fetch/fetchAll逐行或获取全部结果集(4.0版本以上)
        'fetch_mode'  => true,
    ];

    /**
     * 连接对象
     * @access protected
     * @var object
     */
    protected $connect = [];

    /**
     * 表名
     * @access private
     * @var string
     */
    private $table;

    /**
     * 表前缀
     * @access private
     * @var string
     */
    private $prefix = '';

    /**
     * 字段
     * @access private
     * @var string
     */
    private $field = '*';

    /**
     * 关联表
     * @access private
     * @var string
     */
    private $join = '';

    /**
     * 条件
     * @access private
     * @var array
     */
    private $where = '';

    /**
     * 分组
     * @access private
     * @var string
     */
    private $group = '';

    /**
     * 分组查询
     * @access private
     * @var string
     */
    private $having = '';

    /**
     * 排序
     * @access private
     * @var string
     */
    private $order = '';

    /**
     * 记录行数
     * @access private
     * @var string
     */
    private $limit = '';

    /**
     * 查询类型 AND|OR
     * @access private
     * @var string
     */
    private $sql = '';

    /**
     * 数据库操作的错误
     * @access private
     * @var string
     */
    private $dbError = '';

    /**
     * 数据库操作的错误的输出方式 1直接返回 2报错异常
     * @access private
     * @var string
     */
    private $errorType = 1;

    /**
     * 是否过滤执行值
     * @access private
     * @var array
     */
    private $isFilter = 1;

    /**
     * 数据库执行的值
     * @access private
     * @var array
     */
    private $execute = [];

    /**
     * 初始化
     * @access public
     * @param string $conf 配置
     * @return void
     */
    public function __construct(string $type = 'mysql')
    {
        $config             = [];
        $dbconfig           = config('database');
        $config['host']     = $dbconfig['connections'][$type]['host'];
        $config['user']     = $dbconfig['connections'][$type]['username'];
        $config['password'] = $dbconfig['connections'][$type]['password'];
        $config['database'] = $dbconfig['connections'][$type]['database'];
        $config['port']     = $dbconfig['connections'][$type]['port'];
        $config['charset']  = $dbconfig['connections'][$type]['charset'];
        $config['prefix']   = $dbconfig['connections'][$type]['prefix'];

        $this->prefix = $dbconfig['connections'][$type]['prefix'];

        $this->config = array_merge($this->config, $config);

        //初始化
        $this->init();
    }

    /**
     * 初始化
     * @access public
     * @return object
     */
    public function init()
    {
    }

    /**
     * 创建连接
     * @access public
     * @return object
     */
    public function create()
    {
        $data = [];
        //创建连接
        $conn = new \Co\MySQL();
        $res  = $conn->connect($this->config);dd($res);
        if ($res) {
            $data = $conn;
        } else {
            $this->setDbError();
        }
        return $data;
    }

    /**
     * 完整表名
     * @access public
     * @param string $table 表名
     * @return object
     */
    public function table(string $table = '')
    {
        $table = trim($table);
        $data  = explode(' ', $table);
        $alias = isset($data[1]) ? ' ' . $data[1] : '';

        $this->table = '`' . $data[0] . '`' . $alias;
        return $this;
    }

    /**
     * 不带前缀表名
     * @access public
     * @param string $name 表名
     * @return object
     */
    public function name(string $name = '')
    {
        $table = trim($name);
        $data  = explode(' ', $table);
        $alias = isset($data[1]) ? ' ' . $data[1] : '';

        $this->table = '`' . $this->prefix . $data[0] . '`' . $alias;
        return $this;
    }

    /**
     * 字段
     * @access public
     * @param string $field 字段
     * @return object
     */
    public function field(string $field = '')
    {
        if (empty($field)) {
            $field = '*';
        }
        $this->field = trim($field);
        return $this;
    }

    /**
     * 关联表
     * @access public
     * @param string $table 表
     * @param string $condition 条件
     * @param string $type 连表类型
     * @return object
     */
    public function join(string $table, string $condition, string $type = 'left')
    {
        if ($type == 'right') {
            $join = ' RIGHT JOIN';
        } elseif ($type == 'inner') {
            $join = ' INNER JOIN';
        } else {
            $join = ' LEFT JOIN';
        }

        $table = trim($table);
        $data  = explode(' ', $table);
        $alias = isset($data[1]) ? ' ' . $data[1] : '';

        $str = $this->join;
        if (empty($str)) {
            $this->join = $join . ' `' . $data[0] . '`' . $alias . ' ON ' . trim($condition);
        } else {
            $this->join = $str . $join . ' `' . $data[0] . '`' . $alias . ' ON ' . trim($condition);
        }

        return $this;
    }

    /**
     * 条件
     * @access public
     * @param array $condition 条件
     * @return object
     */
    public function where($condition = [])
    {
        $this->where = $condition;
        return $this;
    }

    /**
     * 分组
     * @access public
     * @param string $group 分组
     * @return object
     */
    public function group(string $group)
    {
        $this->group = ' GROUP BY ' . trim($group);
        return $this;
    }

    /**
     * 分组条件
     * @access public
     * @param string $having 条件
     * @return object
     */
    public function having($condition = [])
    {
        $this->having = $condition;
        return $this;
    }

    /**
     * 排序
     * @access public
     * @param string $order 排序
     * @return object
     */
    public function order(string $order)
    {
        $this->order = ' ORDER BY ' . trim($order);
        return $this;
    }

    /**
     * 记录行数
     * @access public
     * @param int $rows 记录行
     * @param int $offset 偏移量
     * @return object
     */
    public function limit(int $rows, int $offset = 0)
    {
        if ($offset == 0) {
            $limit = $rows;
        } else {
            $limit = $offset . ',' . $rows;
        }
        $this->limit = ' LIMIT ' . $limit;
        return $this;
    }

    /**
     * 开启事务(同一个MySQL连接对象，同一时间只能启动一个事务)
     * @access public
     * @return bool
     */
    public function begin(): bool
    {
        return $this->connect->begin();
    }

    /**
     * 提交事务
     * @access public
     * @return bool
     */
    public function commit(): bool
    {
        return $this->connect->commit();
    }

    /**
     * 回滚事务
     * @access public
     * @return bool
     */
    public function rollback(): bool
    {
        return $this->connect->rollback();
    }

    /**
     * 插入数据
     * @access public
     * @param array|string $data 插入的数据
     * @param bool $getId 是否获取主键ID
     * @return int
     */
    public function insert($data = [], bool $getId = false): int
    {
        $num = 0;
        $f   = '';
        if (!empty($data)) {
            $sql = $this->rule($data);

            $this->reConnect();
            //预处理
            $stmt = $this->connect->prepare($sql);
            if ($stmt == false) {
                $this->setDbError();
            } else {
                if ($this->isFilter == 1) {
                    $list = $this->execute;
                } else {
                    $list = [];
                }
                $res = $stmt->execute($list);
                if ($res == false) {
                    $this->setDbError();
                } else {
                    if ($getId) {
                        $num = $this->connect->insert_id;
                    } else {
                        $num = $this->connect->affected_rows;
                    }
                }
            }
        } else {
            $this->setDbError(2);
        }

        $this->clear();
        return $num;
    }

    /**
     * 插入数据
     * @access public
     * @param array $datas 插入的数据
     * @param bool $getId 是否获取主键ID
     * @return array|int
     */
    public function insertAll($datas = [], bool $getId = false)
    {
        $list = [];
        $f    = '';
        if (!empty($datas)) {
            $offset = array_key_first($datas);
            $sql    = $this->rule($datas[$offset]);

            $this->reConnect();
            $stmt = $this->connect->prepare($sql);
            if ($stmt == false) {
                $this->setDbError();
            } else {
                foreach ($datas as $k => $data) {
                    $res = $stmt->execute(array_values($data));
                    if ($res == false) {
                        $list[$k] = '[' . $this->connect->errno . ']' . $this->connect->error;
                    } else {
                        if ($getId) {
                            $list[$k] = $this->connect->insert_id;
                        } else {
                            $list = (int)$list + $this->connect->affected_rows;
                        }
                    }
                }
            }
        } else {
            $this->setDbError(2);
        }

        $this->clear();
        return $list;
    }

    /**
     * 更新数据
     * @access public
     * @param array|string $data 插入的数据
     * @return int
     */
    public function update($data)
    {
        $num = 0;
        $f   = '';
        if (!empty($data)) {
            $sql = $this->rule($data, 2);

            $this->reConnect();
            //预处理
            $stmt = $this->connect->prepare($sql);

            if ($stmt == false) {
                $this->setDbError();
            } else {
                if ($this->isFilter == 1) {
                    $list = $this->execute;
                } else {
                    $list = [];
                }
                $res = $stmt->execute($list);
                if ($res == false) {
                    $this->setDbError();
                } else {
                    $num = $this->connect->affected_rows;
                }
            }
        } else {
            $this->setDbError(2);
        }

        $this->clear();
        return $num;
    }

    /**
     * 查询单行记录
     * @access public
     * @param int $type 类型 1单条 2多条 3count计算个数
     * @param string $field 字段 用于取出指定字段
     * @return array|null
     */
    public function find(int $type = 1, string $field = '')
    {
        if ($type == 1 || $type == 3)
            $this->limit(1);
        if ($type == 3)
            $this->field('COUNT(*) AS kk_count');
        if (!empty($field))
            $this->field($field);
        $sql = $this->rule([], 3);

        $data = [];
        $this->reConnect();

        //预处理
        $stmt = $this->connect->prepare($sql);
        if ($stmt == false) {
            $this->setDbError();
        } else {
            if ($this->isFilter == 1) {
                $list = $this->execute;
            } else {
                $list = [];
            }
            $res = $stmt->execute($list);
            if ($res == false) {
                $this->setDbError();
            } else {
                if ($type == 2) {
                    do {
                        $data = $stmt->fetchAll();
                    } while ($stmt->nextResult());
                } else {
                    do {
                        $data = $stmt->fetch();
                    } while ($stmt->nextResult());

                    if ($type == 3) {
                        $data = $data['kk_count'];
                    }
                }
            }
        }

        $this->clear();
        return $data;
    }

    /**
     * 查询多行记录
     * @access public
     * @return array
     */
    public function select()
    {
        return $this->find(2);
    }

    /**
     * 查询行数
     * @access public
     * @return int
     */
    public function count()
    {
        return $this->find(3);
    }

    /**
     * 查询行数
     * @access public
     * @param string $field 字段 用于取出指定字段
     * @return int
     */
    public function value(string $field = '')
    {
        if (!empty($field)) {
            return $this->find(1, $field)[$field];
        } else {
            return '';
        }
    }

    /**
     * 原生SQL查询
     * @access public
     * @param string $sql sql语句
     * @return array|bool
     */
    public function query($sql = '')
    {
        $this->reConnect();
        $data = $this->connect->query($sql);
        return $data;
    }

    /**
     * 获取最后一条SQL语句
     * @access public
     * @param string $sql sql语句
     * @return array|bool
     */
    public function getLastSql($sql = '')
    {
        return $this->sql;
    }

    /**
     * 获取最后一次执行SQL错误
     * @access public
     * @return string
     */
    public function getDbError()
    {
        return $this->dbError;
    }

    /**
     * 设置SQL的错误
     * @access private
     * @param $type string 错误信息类别
     * @return void
     */
    private function setDbError($type = 1)
    {
        if ($type == 2) {
            $msg = 'param is empty';
        } else {
            if (isset($this->connect->errno) && $this->connect->error) {
                $msg = '[' . $this->connect->errno . ']' . $this->connect->error;
            } else {
                $msg = '';
            }
        }

        if ($this->errorType == 1) {
            $this->dbError = $msg;
        } else {
            throw new \Exception($msg);
        }
    }

    /**
     * 断线重连
     * @access private
     * @return void
     */
    private function reConnect()
    {
        if (!isset($this->connect->connected) || $this->connect->connected === false) {
            $this->connect = $this->create();
        }
    }

    /**
     * 获取where条件
     * @access private
     * @param array|string $condition 操作的数据
     * @return string
     */
    private function getWhere($condition = ''): string
    {
        $where = '';
        //规则 目前只支持 _string AND 模式 可以扩展...
        if (!empty($condition)) {
            $filter = ['>', '<', '=', '>=', '<=', '<>', '!='];
            $type   = 'AND';

            if (is_array($condition)) {
                //执行的值
                $execute = [];
                foreach ($condition as $key => $vo) {
                    if (!empty($vo)) {
                        if ($key == '_string') {
                            $where .= $vo . ' ' . $type . ' ';
                        } else {
                            //前缀
                            $field = '';
                            //字段
                            $key = trim($key);
                            //判断条件
                            $condi = '=';
                            //字段值
                            $value = $vo;

                            if (strpos($key, '.') !== false) {
                                $kdata = explode('.', $key);
                                if (isset($kdata[1])) {
                                    $field = $kdata[0] . '.`' . $kdata[1] . '`';
                                } else {
                                    $field = '`' . $kdata[0] . '` ';
                                }
                            } else {
                                $field = '`' . $key . '`';
                            }

                            if (is_array($vo)) {
                                if (isset($vo[0])) {
                                    if (is_string($vo[0])) {
                                        //转义字符
                                        $value = $this->connect->escape($vo[0]);
                                        $value = "'" . $value . "'";
                                    } else {
                                        $value = $vo[0];
                                    }
                                    $execute[] = $vo[0];
                                }

                                if (isset($vo[1]) && in_array($vo[1], $filter)) {
                                    $condi = $vo[1];
                                }
                                if (isset($vo[2]) && in_array($vo[2], ['AND', 'OR'])) {
                                    $type = $vo[2];
                                }
                            } else {
                                if (is_string($vo)) {
                                    //转义字符
                                    $value = $this->connect->escape($vo);
                                    $value = "'" . $value . "'";
                                }
                                $execute[] = $vo;
                            }

                            if ($this->isFilter == 1) {
                                $where .= $field . ' ' . $condi . ' ? ' . $type . ' ';
                            } else {
                                $where .= $field . ' ' . $condi . ' ' . $value . ' ' . $type . ' ';
                            }
                        }
                    }
                }

                //合并执行参数
                $this->execute = array_merge($this->execute, $execute);
                $where         = rtrim($where, ' ' . $type . ' ') . '';
            } else {
                $where = $condition;
            }
        }
        //var_dump($where);

        return $where;
    }

    /**
     * 规则
     * @access private
     * @param array $datas 操作的数据
     * @param int $type 类型 1添加 2更新 3查询
     * @return string
     */
    private function rule($data = [], int $type = 1): string
    {
        $set    = '';
        $group  = '';
        $having = '';
        $order  = '';
        $limit  = '';
        $join   = '';

        //按照顺序拼凑
        if ($type == 1) {
            $sql   = 'INSERT INTO ';
            $set   = $this->getSet($data);
            $where = $this->getSetWhere();
        } elseif ($type == 2) {
            $sql   = 'UPDATE ';
            $set   = $this->getSet($data);
            $where = $this->getSetWhere();
        } else {
            $sql = 'SELECT ' . $this->field . ' FROM ';

            //连表
            $join = $this->join;
            //条件
            $where = $this->getSetWhere();
            //分组
            $group = $this->group;
            //分组查询
            $having = $this->getWhere($this->having);
            if (!empty($having)) {
                $having = ' HAVING ' . $having;
            }
            //排序
            $order = $this->order;
            //行数
            $limit = $this->limit;
        }

        $sql .= $this->table . $set . $join . $where . $group . $having . $order . $limit;

        $this->sql = $sql;
        return $sql;
    }

    /**
     * SET规则的拼凑
     * @access private
     * @param array $datas 操作的数据
     * @return string
     */
    private function getSet($data)
    {
        $set = '';
        if (!empty($data)) {
            if (is_array($data)) {
                $execute = [];
                foreach ($data as $key => $vo) {
                    if (is_string($vo)) {
                        //转义字符
                        $v = $this->connect->escape($vo);
                        $v = "'" . $v . "'";
                    } else {
                        $v = $vo;
                    }
                    $execute[] = $vo;
                    if ($this->isFilter == 1) {
                        $set .= '`' . $key . "` = ?, ";
                    } else {
                        $set .= '`' . $key . "` = " . $v . ", ";
                    }
                }

                $set = ' SET ' . rtrim($set, ', ');

                //合并执行参数
                $this->execute = array_merge($this->execute, $execute);
            } else {
                $set = ' SET ' . $data;
            }
        }
        return $set;
    }

    /**
     * Where规则的拼凑
     * @access private
     * @return string
     */
    private function getSetWhere()
    {
        $where = $this->getWhere($this->where);
        if (!empty($where)) {
            $where = ' WHERE ' . $where;
        }
        return $where;
    }


    /**
     * 初始化对象
     * @access private
     * @return void
     */
    private function clear()
    {
        $this->table   = '';
        $this->field   = '*';
        $this->join    = '';
        $this->where   = '';
        $this->group   = '';
        $this->having  = '';
        $this->order   = '';
        $this->limit   = '';
        $this->execute = [];
    }
}
